P05-1. L7 트래픽 엔지니어링과 서킷 브레이커 - 이론 개념

Part 05 (이론): L7 트래픽 엔지니어링과 서킷 브레이커 (HTTPRoute · OutlierDetection · UO)

핵심 학습 목표

  1. 왜 기본 쿠버네티스 Deployment + Service 조합만으로는 정교한 카나리 배포(90:10 가중치 분할) 나 특정 사용자(HTTP 헤더) 타겟 라우팅이 불가능한지 이해한다.
  2. Ambient Mesh의 Waypoint Proxy 위에서 쿠버네티스 표준 Gateway API (HTTPRoute) 와 Istio VirtualService / DestinationRule 이 각각 어떤 역할을 맡는지 비교한다.
  3. 마이크로서비스의 도미노 붕괴(연쇄 장애)를 막는 3대 방어 기제인 타임아웃(Timeout), 재시도(Retry), 서킷 브레이커(Circuit Breaker) 의 동작 원리를 설명할 수 있다.
  4. 서킷 브레이커의 두 가지 축인 커넥션 풀 제한(ConnectionPool → UO 플래그) 과 불량 파드 자동 격리(OutlierDetection) 의 차이를 완벽히 구분한다.


1. 왜 기본 쿠버네티스만으로는 안전한 신규 버전 출시가 어려울까?

우리가 열심히 개발한 주문 서비스 order-api의 v1 파드 2개가 운영 환경에서 돌고 있다고 해봅시다. 이제 결제 로직을 새로 고친 v2 버전을 출시하려고 합니다. 만약에 v2에 치명적인 버그가 숨어 있다면 어떻게 될까요? 전체 고객의 절반이 즉시 결제 실패를 겪게 됩니다.

그래서 현업에서는 "처음에는 전체 트래픽의 딱 10%만(혹은 사내 QA 팀원들의 요청만) v2로 보내 보고, 에러가 없으면 50% → 100%로 서서히 늘리자!" 는 카나리 배포(Canary Release) 전략을 씁니다.

용어 정리 — 카나리 배포 (Canary Release) 와 배포(Deployment) vs 릴리스(Release)

  • 카나리(Canary)의 유래: 과거 석탄 광부들은 탄광에 들어갈 때 일산화탄소 등 유독가스에 사람보다 훨씬 민감한 '카나리아(Canary)' 새를 새장에 넣어 먼저 데리고 들어갔습니다. 카나리아가 위험 신호를 보내면 광부들이 즉시 대피해 목숨을 구할 수 있었기 때문입니다. 소프트웨어에서도 새 버전(v2)에 전체 트래픽의 극소량(예: 5~10%)만 먼저 흘려보내어 장애 여부를 미리 감지하는 기법을 카나리 배포라고 부릅니다.
  • 배포(Deployment) vs 릴리스(Release)의 분리:
    • 배포(Deployment): 새 버전의 파드(v2)를 클러스터에 실제로 띄워 놓는 행위입니다.
    • 릴리스(Release): 실제 고객의 요청(트래픽)을 그 새 파드로 흘려보내는 행위입니다.
    • 기본 쿠버네티스에서는 파드가 뜨자마자(Ready) 무조건 파드 개수 비율대로 트래픽을 받기 때문에 배포와 릴리스가 강제로 묶여 있습니다. 반면 서비스 메시(Waypoint)는 파드가 몇 개 떠 있든 상관없이 프록시에서 트래픽 비율(0% ~ 100%)을 마음대로 조절할 수 있어 배포와 릴리스를 완벽히 분리해 줍니다!

기본 쿠버네티스 vs Ambient Mesh (Waypoint) 카나리 비교

flowchart TB
    subgraph K8S_NATIVE["1. 기본 쿠버네티스 Service (파드 개수 비율에 종속됨)"]
        direction LR
        S1["Service (order-api)"] -->|"무조건 1/3 (33.3%)"| P1["v1 파드 #1"]
        S1 -->|"무조건 1/3 (33.3%)"| P2["v1 파드 #2"]
        S1 -->|"무조건 1/3 (33.3%)
(5%만 보내려면 파드 20개 필요!)"| P3["v2 신규 파드 #1"] end subgraph MESH_WAYPOINT["2. Ambient Mesh Waypoint (파드 개수와 무관한 L7 정밀 제어)"] direction LR WP["Waypoint Proxy (Envoy L7)"] -->|"가중치 90% 배분"| V1["order-api-v1 Service
(파드 1~2개만 있어도 됨)"] WP -->|"가중치 10% 배분
또는 x-canary: true 헤더만 100%"| V2["order-api-v2 Service
(파드 1개만 있어도 정확히 10%!)"] end

2. Ambient Mesh에서 L7 라우팅을 설정하는 두 가지 표준 무기

Part 03에서 우리는 네임스페이스에 Waypoint Proxy 를 띄우자마자, Keep-Alive 연결 위에서도 두 파드로 요청이 정확히 번갈아 나뉘는 것을 확인했습니다.
이제 이 Waypoint에게 구체적인 교통정리 명령을 내릴 차례입니다. Istio Ambient Mesh에서는 두 가지 방식을 모두 지원합니다.

구분 1. Kubernetes 표준 Gateway API (HTTPRoute) 2. Istio 고유 API (VirtualService & DestinationRule)
표준 여부 쿠버네티스 공식 표준 (gateway.networking.k8s.io/v1) Istio 전용 CRD (networking.istio.io/v1)
강점 및 주요 용도 가중치 기반 카나리 라우팅(weight: 90 / 10), 헤더/경로 기반 분기, 기본 타임아웃 설정 등 표준적인 L7 라우팅에 가장 깔끔하고 권장됨 결함 주입(Fault Injection), 정밀한 재시도 조건(retryOn), 서킷 브레이커(ConnectionPool / OutlierDetection) 등 고급 회복탄력성 기능 제어
Waypoint 연결 방법 parentRefs 에 대상 Service (kind: Service, name: order-api)를 지정하면 Waypoint가 자동으로 규칙을 읽어 적용! 기존과 동일하게 hosts: ["order-api"]를 지정하면 Waypoint가 자동으로 규칙을 읽어 적용!
실무에서는 어떻게 조합하나요?

  • 일반적인 카나리 가중치 분배(90:10)나 헤더/URL 분기는 쿠버네티스 표준인 HTTPRoute 로 작성하는 것이 최신 트렌드입니다.
  • 반면 카오스 테스트를 위한 결함 주입(Fault Injection) 이나 서킷 브레이커(DestinationRule) 는 아직 표준 Gateway API 스펙보다 Istio의 VirtualService / DestinationRule이 훨씬 세밀한 옵션을 제공하므로 필요에 따라 함께 사용합니다. 이번 Part 05 실습에서는 두 가지를 모두 직접 다뤄봅니다!


3. 연쇄 장애(Cascading Failure)와 회복탄력성 3총사: Fault · Timeout · Retry

마이크로서비스 환경에서 가장 무서운 재앙은 '죽은 서버'가 아니라 '죽지도 않고 대답을 한없이 늦게 주는(Slow) 서버' 입니다.

sequenceDiagram
    autonumber
    participant User as 고객 브라우저
    participant Front as 프론트엔드 파드
    participant Order as order-api 파드
    participant PG as 결제/DB 서버 (장애로 30초 지연!)

    User->>Front: 주문하기 클릭
    Front->>Order: POST /order 호출
    Order->>PG: 결제 요청 (응답 없이 30초 동안 멈춰 있음...)
    Note over Front,Order: 타임아웃이 없으면?
Order 파드와 Front 파드의 작업 스레드·메모리가
30초 동안 붙잡혀 고갈됨 (도미노 전체 붕괴!)

이 도미노 붕괴(연쇄 장애, Cascading Failure)를 막기 위해 서비스 메시는 코드 수정 없이 설정 파일만으로 다음 3가지 방어막을 제공합니다.

용어 정리 — 회복탄력성(Resilience) 3대 기능

  1. 결함 주입 (Fault Injection — 예방 주사):
    • 실제 운영 장애가 터지기 전에 우리 시스템의 방어막이 제대로 작동하는지 시험해 보기 위해, 프록시(Waypoint)에서 일부러 특정 비율의 요청에 지연 시간(delay, 예: 3초 멈춤)을 주거나 강제 에러(abort, 예: HTTP 500)를 발생시키는 카오스 엔지니어링 기능입니다.
  2. 타임아웃 (Timeout — 손절매 타이머):
    • "목적지 파드가 1초(1s) 안에 응답하지 않으면 더 이상 기다리지 말고 즉시 연결을 끊어라(504 Gateway Timeout)!" 라고 제한 시간을 걸어 스레드 고갈을 막는 기능입니다.
  3. 자동 재시도 (Retries — 재도전):
    • 네트워크가 잠깐 흔들리거나 특정 파드 하나만 일시적으로 503 에러를 냈을 때, 사용자에게 에러 화면을 바로 보여주는 대신 Waypoint 프록시가 눈치채지 못하게 건강한 옆 파드로 2~3번 재시도하여 성공 응답을 받아내는 기능입니다.


4. 서킷 브레이커 (Circuit Breaker): 우리 집 두꺼비집을 내려라!

타임아웃과 재시도만으로는 부족할 때가 있습니다. 만약 order-api 파드 2개 중 1개(파드 B)의 메모리가 꼬여서 계속 500 Internal Server Error를 뿜어내고 있거나, 갑자기 블랙프라이데이 트래픽이 몰려 파드가 감당할 수 있는 동시 처리 한계를 넘었다면 어떻게 해야 할까요?

계속해서 죽어가는 파드에게 트래픽을 보내거나 재시도를 폭격하면 아예 서버가 영구 사망합니다. 이때 작동하는 것이 바로 가정집의 누전차단기(두꺼비집) 와 똑같은 원리인 서킷 브레이커(Circuit Breaker) 입니다!

Istio의 DestinationRule 리소스 안에는 서킷 브레이커를 구현하는 두 가지 핵심 장치가 들어 있습니다.

flowchart TB
    subgraph CB1["장치 1 · ConnectionPool (과부하 차단기 → UO 플래그)"]
        REQ1["동시 요청 폭주!"] --> CHECK1{"최대 연결/대기열 한도 초과?
(maxConnections: 1
http1MaxPendingRequests: 1)"} CHECK1 -->|"한도 이내"| OK1["정상 전달 (200 OK)"] CHECK1 -->|"한도 초과!"| TRIP1["서버로 보내지 않고 즉시 차단!
HTTP 503 + x-envoy-overloaded: true
(로그 플래그: UO - Upstream Overflow)"] end subgraph CB2["장치 2 · OutlierDetection (불량 파드 자동 격리)"] REQ2["파드 2개(A, B)로 분산 중"] --> CHECK2{"파드 B에서 연속 5xx 에러 발생?
(consecutive5xxErrors: 2)"} CHECK2 -->|"정상"| OK2["A, B 모두에게 50:50 분배"] CHECK2 -->|"연속 2회 에러 감지!"| EJECT["파드 B를 30초간 EDS 명단에서 퇴출(Eject)!
트래픽 100%를 건강한 파드 A로만 자동 우회!"] end
용어 정리 — ConnectionPool vs OutlierDetection & UO 플래그

  1. 커넥션 풀 한도 제한 (trafficPolicy.connectionPool):
    • 역할: 백엔드 파드로 동시에 들어갈 수 있는 최대 TCP 연결 수(tcp.maxConnections) 와 최대 대기 요청 수(http.http1MaxPendingRequests, http2MaxRequests) 에 상한선을 긋습니다.
    • 차단 동작 (Fast Fail & UO 플래그): 이 상한선을 넘는 폭주 트래픽이 들어오면, 프록시는 백엔드 파드가 죽지 않도록 보호하기 위해 패킷을 파드로 보내지도 않고 프록시 선에서 즉시 503 Service Unavailable로 튕겨냅니다.
    • 이때 응답 헤더에 x-envoy-overloaded: true 가 찍히며, Envoy 액세스 로그(Part 09에서 상세 분석)에는 "백엔드 용량이 넘쳐서 서킷 브레이커가 내려갔다" 는 뜻의 UO (Upstream Overflow) 플래그가 기록됩니다!
  2. 이상치 탐지 / 불량 파드 퇴출 (trafficPolicy.outlierDetection):
    • 역할: 여러 개의 파드 중 유독 에러(5xx)를 연속으로 뿜어내는 '불량 파드(Outlier)'를 실시간으로 색출해 로드밸런싱 명단(EDS)에서 일시적으로 쫓아내는(Ejection) 기능입니다.
    • 주요 설정값:
      • consecutive5xxErrors: 2: 연속으로 5xx 에러를 2번 내면 불량 파드로 판정하라!
      • interval: 10s: 10초 주기로 불량 파드를 검사하라!
      • baseEjectionTime: 30s: 적발된 불량 파드는 30초 동안 자숙(격리) 시켜서 아예 요청을 보내지 마라! (30초 뒤에 다시 명단에 복귀시켜 보고, 또 에러를 내면 30초×2=60초, 90초로 격리 시간을 점점 늘립니다!)
      • maxEjectionPercent: 100: 전체 파드의 몇 %까지 격리를 허용할지 지정합니다.


🔗 다음 단계 (실습 검증)

이론으로 배운 L7 트래픽 엔지니어링과 서킷 브레이커를 이제 우리의 docker-desktop 클러스터(Waypoint Proxy 환경)에서 직접 돌려봅니다!

  1. order-api의 신규 버전인 v2 파드를 띄우고, 쿠버네티스 표준 HTTPRoute 로 정확히 90:10 비율의 카나리 가중치 분산과 x-canary: true 헤더 분기를 검증하고,
  2. Fault Injection(3초 지연) + Timeout(1초 손절) 조합으로 504 Gateway Timeout 방어막이 작동하는 것을 확인하고,
  3. DestinationRule 서킷 브레이커를 걸어 동시 요청 시 x-envoy-overloaded: true(503 UO) 가 터지는 장면까지 100% 실측합니다!

네비게이션

이전 목록 다음
◀ Part 04 실습 목록으로 Part 05 실습 ▶